package top.hekun.study.core.visitor.method.complex;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

import java.io.PrintStream;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Opcodes.ILOAD;

/**
 * 〈输出打印语句的代码〉    System.out.println("执行 HelloWordComplex#add3 方法");
 *
 * @author: Sakura
 * @date: 2023/5/13
 */
public class MyMethodVisitor4 extends GeneratorAdapter {
    private String thisClassName = this.getClass().getSimpleName();

    public MyMethodVisitor4(MethodVisitor methodVisitor, Method method) {
        super( Opcodes.ASM9, methodVisitor, ACC_PUBLIC, method.getName(), method.getDescriptor());

    }
    

    /**
     * 实现编码 public int testLambda(int a, int b) { BinaryOperator<Integer> function = ( ai,  bi)-> Math.max(ai,bi); int
     * apply = function.apply(a, b); return apply; }
     */
    public void code() {
        visitCode();
        // 编写代码体
        getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
        push(thisClassName + ", 准备生成 Lambda 表达式!!");
        invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)"));


        /**
         * tag–此句柄指定的字段或方法的类型。
         * 必须是Opcodes.H_GETFIELD、Opcodes.H.GETFIELD、Opcodes.H.PUTSTATIC、Opcodes.H.INVOKEVIRTUAL、Opcodes.H.INVOKESTIC、Opcodes.H.INVOKESPECIAL、Opcode.H.NEWINVOKESSPECIAL或Opcodes.H_INVOKEIFACE。
         *  owner–拥有此句柄指定的字段或方法的类的内部名称（请参见Type.getInternalName（））。
         *  name–此句柄指定的字段或方法的名称。
         *  descriptor–此句柄指定的字段或方法的描述符。
         *  isInterface–所有者是否为接口
         *
         *   加载 `j`ava/lang/invoke/LambdaMetafactory # metafactory ` 方法
         */
        // 第1点，Handle实例的创建
        Handle bootstrapMethodHandle = new Handle(H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory",
                "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;" +
                        "Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false);
        // 第2点，MethodVisitor.visitInvokeDynamicInsn()方法的调用
        visitInvokeDynamicInsn("apply", "()Ljava/util/function/BiFunction;",
                bootstrapMethodHandle,
                Type.getType("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"),
                new Handle(H_INVOKESTATIC, "java/lang/Math", "max", "(II)I", false),
                Type.getType("(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;")
        );


        // 存储 上面的动态调用指令到 3号位置
        visitVarInsn(ASTORE, 3);
        // 加载 3号 位置对象 (Lambda 动态调用)
        visitVarInsn(ALOAD, 3);
        // 加载1号 位置对象( 第一个参数)
        visitVarInsn(ILOAD, 1);
        // 转化为封装类型
        visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
        // 加载2号 位置对象( 第二个参数)
        visitVarInsn(ILOAD, 2);
        // 转化为封装类型
        visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
        // 调用接口方 方法 BinaryOperator接口的  apply 方法
        visitMethodInsn(INVOKEINTERFACE, "java/util/function/BinaryOperator", "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
        // 检查栈顶的数值 是不是Integer 类型 如果不是 会抛出异常 , 如果是null 则不处理
        visitTypeInsn(CHECKCAST, "java/lang/Integer");
        // 数据类型转换
        visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
        // 将结果存在 4 号位置
        visitVarInsn(ISTORE, 4);
        // 取出 数据
        visitVarInsn(ILOAD, 4);

        returnValue();
        endMethod();
    }


}
